import numpy as np
def non_max_suppression(boxes, max_bbox_overlap, scores=None):
    """
    删除掉某些检测重复的
    Original code from [1]_ has been adapted to include confidence score.

    .. [1] http://www.pyimagesearch.com/2015/02/16/
           faster-non-maximum-suppression-python/

    Parameters
    ----------
    boxes : {ndarray:(10,4)}
        坐标格式为(x, y, width, height).
    max_bbox_overlap : float
        ROI超过这个值得会被抑制
    scores : {ndarray:(10,)}
        目标检测器生成的置信度得分
    -----------
    Returns
    List[int]
        Returns indices of detections that have survived non-maxima suppression.
        返回在非极大值抑制中幸存下来的检测索引数组。
    """
    if len(boxes) == 0:#如果box为空（为空只可能发生在不满足置信度阈值条件下）
        return []

    boxes = boxes.astype(np.float)
    pick = []   #输出剩余数组的索引
    #将格式(tl_x,tl_y,box_w,box_h)转换为(tl_x,tl_y,br_x,br_y)的格式
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2] + boxes[:, 0]
    y2 = boxes[:, 3] + boxes[:, 1]
    #求所有box的面积，+1是因为索引是从0开始的
    area = (x2 - x1 + 1) * (y2 - y1 + 1)
    if scores is not None:
        idxs = np.argsort(scores)#idxs是从小到大的排列，所以idxs的最后一个元素对应的是最大置信度的索引
    else:
        idxs = np.argsort(y2)

    while len(idxs) > 0:#idxs是score从小到大排序的索引
        last = len(idxs) - 1    #最后一个
        i = idxs[last]          #最后一个的索引值，最后一个索引值意味着是置信度最高的一个box对象
        pick.append(i)          #将最大置信度的索引，放入pick
        #xx1,yy1,xx2,yy2分别是最大置信度的box和其他box的相交矩形的坐标，这种做法，牛逼
        xx1 = np.maximum(x1[i], x1[idxs[:last]])#较大的x1，一个坐标与剩下的所有坐标比较大小，返回较大的 ->{ndarray:(9,)}
        yy1 = np.maximum(y1[i], y1[idxs[:last]])#较大的y1，->{ndarray:(9,)}
        xx2 = np.minimum(x2[i], x2[idxs[:last]])#较小的x2，->{ndarray:(9,)}
        yy2 = np.minimum(y2[i], y2[idxs[:last]])#较小的y2，->{ndarray:(9,)}
        #获取相交矩形的宽高，为0的情况是两个矩形框没有相交
        w = np.maximum(0, xx2 - xx1 + 1)
        h = np.maximum(0, yy2 - yy1 + 1)
        #计算IOU：相交/相并
        overlap = (w * h) / area[idxs[:last]]
        '''
        np.where(overlap > max_bbox_ouverlap)
            1.将其他boxs与该最大置信度的box求IOU，若该IOU大于阈值，则证明是同一个对象，则放入
            待删除列表。 np.where()返回一个tuple，该tuple里是一个ndarray
        [0]
            2.获取该tuple里的ndarray
        np.concatenate(??)
            3.将置信度最大的一个box的索引，与当前要删除的索引拼在一起。可以理解为置信度最大的box和
            证明是同一个对象的bbox的索引(1)，放在一起，等待被删除。返回一个ndarray
        np.delete(??)
            4.根据待删除的ndarray，从idxs中删除这些索引，但是记住是以待删除ndarray里的数，作为
            idxs的索引来删除。
            比如 idxs = [9,8,7,6,5,4,3,2,0,1] 待删除ndarray=[3,7]
            则返回=[9,8,7,5,4,3,0,1]
        '''
        idxs = np.delete(
            idxs, np.concatenate(
                ([last], np.where(overlap > max_bbox_overlap)[0])))
    return pick
